package io.github.sceneview.ar.node

import com.google.android.filament.Engine
import com.google.ar.core.AugmentedImage
import com.google.ar.core.AugmentedImage.TrackingMethod
import com.google.ar.core.AugmentedImageDatabase
import com.google.ar.core.TrackingState

/**
 * AR Augmented Image positioned 3D model node
 *
 * ARCore can estimate the pose of the physical image at runtime as soon as ARCore detects the
 * physical image, without requiring the user to move the device to view the physical image from
 * different viewpoints. Note that ARCore will refine the estimated size and pose of the physical
 * image as it is viewed from different viewpoints.
 *
 * You can add a single named image to the augmented image database from an Android bitmap per node
 * at runtime or can manage your own [AugmentedImageDatabase] and configure the session with it:
 * Call [com.google.ar.core.Config.setAugmentedImageDatabase], provide the image name to the node
 * and skip bitmap parameter
 *
 * An image database supports up to 1000 images and can be generated by the `arcoreimg` command-line
 * database generation tool provided in the ARCore SDK, or dynamically created at runtime by adding
 * individual images to an AugmentedImageDatabase.
 *
 * Only one image database can be active in a session. Any images in the currently active image
 * database that have a TRACKING/PAUSED state will immediately be set to the STOPPED state if a
 * different or null image database is made active in the current session Config
 */
open class AugmentedImageNode(
    engine: Engine,
    /**
     * The augmented image where this node will be placed
     */
    val augmentedImage: AugmentedImage,
    /**
     * Should the node scale depending on the image extents
     */
    var applyImageScale: Boolean = false,
    /**
     * Set the node to be visible only on those tracking methods
     */
    val visibleTrackingMethods: Set<TrackingMethod> = setOf(
        TrackingMethod.FULL_TRACKING, TrackingMethod.LAST_KNOWN_POSE
    ),
    onTrackingStateChanged: ((TrackingState) -> Unit)? = null,
    val onTrackingMethodChanged: ((TrackingMethod) -> Unit)? = null,
    onUpdated: ((AugmentedImage) -> Unit)? = null
) : TrackableNode<AugmentedImage>(
    engine = engine,
    onTrackingStateChanged = onTrackingStateChanged,
    onUpdated = onUpdated
) {

    /**
     * Returns the name of this augmented image.
     *
     * The image name is not guaranteed to be unique.
     */
    val imageName get() = augmentedImage.name

    /**
     * The TrackingState of this Node.
     *
     * Updated on each frame
     */
    open var trackingMethod = TrackingMethod.NOT_TRACKING
        get() = augmentedImage.trackingMethod
        protected set(value) {
            if (field != value) {
                field = value
                updateVisibility()
                onTrackingMethodChanged?.invoke(value)
            }
        }

    override var isVisible
        get() = super.isVisible && trackingMethod in visibleTrackingMethods
        set(value) {
            super.isVisible = value
        }

    init {
        trackable = augmentedImage
    }

    override fun update(trackable: AugmentedImage?) {
        super.update(trackable)

        trackingMethod = augmentedImage.trackingMethod
        if (trackingState == TrackingState.TRACKING &&
            trackingMethod == TrackingMethod.FULL_TRACKING
        ) {
            pose = augmentedImage.centerPose
            if (applyImageScale) {
                scale = scale.copy(
                    x = augmentedImage.extentX,
                    z = augmentedImage.extentZ
                )
            }
        }
    }
}